package com.hyt.it.ogt.kq.service.gov.schedule.utils;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.hyt.it.ogt.kq.service.gov.schedule.model.ExamInfo;
import com.hyt.it.ogt.kq.service.gov.schedule.model.ExamRoom;

import lombok.Data;

/***
 * 根据考试应答方式和考场科目拆分考试信息
 * 
 */
public class ExamInfoUtils {
    /***
     * 考试任务拆分成多个子任务
     * 拆分原则：
     * 1）相同的考试应答方式
     * 2）按考场场支持的考试科目拆分
     * 
     * @param examInfo
     * @param examRooms
     * @return
     */
    public static List<PairExamInfoAndExamRoom> splitExamInfos(List<ExamInfo> examInfo, List<ExamRoom> examRooms) {
        List<PairExamInfoAndExamRoom> pairExamInfoAndExamRooms = new ArrayList<>();
        List<PairExamInfoAndExamRoom> pairExamTypeExamInfoAndExamRoom = splitExamInfosByExamType(examInfo, examRooms);
        for (PairExamInfoAndExamRoom pairItem : pairExamTypeExamInfoAndExamRoom) {
            List<PairExamInfoAndExamRoom> pairSubjectExamInfoAndExamRoom = splitExamInfosBySubject(
                    pairItem.getPairExamInfos(), pairItem.getPairExamRooms());
            pairExamInfoAndExamRooms.addAll(pairSubjectExamInfoAndExamRoom);
        }
        return pairExamInfoAndExamRooms;
    }

    /**
     * 根据考试应答方式拆分
     * 
     * @param examInfo
     * @param examRooms
     * @return
     */
    public static List<PairExamInfoAndExamRoom> splitExamInfosByExamType(List<ExamInfo> examInfos,
            List<ExamRoom> examRooms) {
        List<PairExamInfoAndExamRoom> pairExamInfoAndExamRooms = new ArrayList<>();
        Set<String> examTypes = examRooms.stream().map(ExamRoom::getExamType).collect(Collectors.toSet());
        for (String examType : examTypes) {
            List<ExamRoom> examRoomsOfType = examRooms.stream()
                    .filter(item -> item.getExamType().equals(examType)).collect(Collectors.toList());
            List<ExamInfo> examInfosOfType = getExamInfoByExamType(examInfos, examType);
            pairExamInfoAndExamRooms.add(new PairExamInfoAndExamRoom(examInfosOfType, examRoomsOfType));
        }
        return pairExamInfoAndExamRooms;
    }

    /**
     * 根据考试科目进行拆分
     * 
     * @param examInfo
     * @param examRooms
     * @return
     */
    public static List<PairExamInfoAndExamRoom> splitExamInfosBySubject(List<ExamInfo> examInfos,
            List<ExamRoom> examRooms) {
        List<PairExamInfoAndExamRoom> pairExamInfoAndExamRooms = new ArrayList<>();
        Set<String> subjectSets = new HashSet<>();
        List<Set<String>> subjectList = new ArrayList<>();
        for (ExamRoom examRoom : examRooms) {
            String subjectStr = String.join(",", examRoom.getSubjects());
            if (subjectSets.contains(subjectStr)) {
                continue;
            }
            subjectSets.add(subjectStr);
            subjectList.add(examRoom.getSubjects());

        }
        List<Set<String>> mergeSubjectList = mergeSubjects(subjectList);
        for (Set<String> subjects : mergeSubjectList) {
            List<ExamRoom> examRoomsOfSubject = examRooms.stream()
                    .filter(item -> setContains(subjects, item.getSubjects())).collect(Collectors.toList());
            List<ExamInfo> examInfosOfSubject = getExamInfoByExamSubject(examInfos, subjects);
            pairExamInfoAndExamRooms.add(new PairExamInfoAndExamRoom(examInfosOfSubject, examRoomsOfSubject));
        }
        return pairExamInfoAndExamRooms;
    }

    /**
     * 合并科目，合并规则如两组subject有交集，则进行合并
     * 
     * @param subjects
     */
    public static List<Set<String>> mergeSubjects(List<Set<String>> subjects) {
        List<Set<String>> mergeSubjects = (subjects);
        while (true) {
            List<Set<String>> sortedSets = mergeSubjects.stream()
                    .sorted(Comparator.comparingInt(item -> ((Set) item).size()).reversed())
                    .collect(Collectors.toList());
            mergeSubjects = new ArrayList<>();
            if (sortedSets.size() == 1) {
                mergeSubjects.add(sortedSets.get(0));
                break;
            }
            Set<String> baseSubjects = sortedSets.get(0);
            mergeSubjects.add(baseSubjects);
            boolean exitMergedSubject = false;
            for (int i = 1; i < sortedSets.size(); i++) {
                if (SimilarUtils.intersection(baseSubjects, sortedSets.get(i))) {
                    baseSubjects.addAll(sortedSets.get(i));
                    exitMergedSubject = true;
                } else {
                    mergeSubjects.add(sortedSets.get(i));
                }
            }
            if (!exitMergedSubject) {
                break;
            }
        }

        return mergeSubjects;
    }

    private static List<ExamInfo> getExamInfoByExamType(List<ExamInfo> examInfos, String examType) {
        List<ExamInfo> filterExamInfos = new ArrayList<>();
        for (ExamInfo examInfo : examInfos) {
            ExamInfo filterExamInfo = new ExamInfo();
            filterExamInfo.setBatchId(examInfo.getBatchId());
            filterExamInfo.setSubjects(examInfo.getSubjects());
            filterExamInfo.setCandidates(examInfo.getCandidates().stream()
                    .filter(item -> item.getExamType().equals(examType)).collect(Collectors.toList()));
            filterExamInfos.add(filterExamInfo);
        }
        return filterExamInfos;
    }

    public static boolean setContains(Set<String> subjects, Set<String> objects) {
        for (String object : objects) {
            if (subjects.contains(object)) {
                return true;
            }
        }
        return false;
    }

    public static String scoreSubjects(List<String> subjects) {

        BitSet score = new BitSet(subjects.size());
        for (int index = 0; index < subjects.size(); index++) {
            if (!subjects.get(index).equalsIgnoreCase("ANY")) {
                score.set(index);
            }
        }
        return score.toString();
    }

    private static List<ExamInfo> getExamInfoByExamSubject(List<ExamInfo> examInfos, Set<String> subjects) {
        List<ExamInfo> filterExamInfos = new ArrayList<>();
        for (ExamInfo examInfo : examInfos) {
            ExamInfo filterExamInfo = new ExamInfo();
            filterExamInfo.setBatchId(examInfo.getBatchId());
            filterExamInfo.setSubjects(examInfo.getSubjects());
            filterExamInfo.setCandidates(examInfo.getCandidates().stream()
                    .filter(item -> subjects.contains(item.getSubject())).collect(Collectors.toList()));
            filterExamInfos.add(filterExamInfo);
        }
        return filterExamInfos;
    }

    @Data
    public static class PairExamInfoAndExamRoom {
        public List<ExamInfo> pairExamInfos;
        public List<ExamRoom> pairExamRooms;

        public PairExamInfoAndExamRoom(List<ExamInfo> pairExamInfos, List<ExamRoom> pairExamRooms) {
            this.pairExamInfos = pairExamInfos;
            this.pairExamRooms = pairExamRooms;
        }
    }
}
